const chalk = require("chalk");
const path = require("path");
const glob = require("glob");
const notifier = require("node-notifier");
const pka = require("../../package.json");
const { NodeSSH } = require("node-ssh");
const zipName = "dist.tgz";
const inquirer = require("inquirer");
const figlet = require("figlet");
const EventLoop = require("./eventloop");
const logger = require("./logger")(getConfig().isLog);

function checkConf(cos) {
  const target = ["script", "localDir", "remote"];
  const remoteConfig = ["remoteDir", "host", "username"];
  if (!cos) {
    logger.error(`中缺少参数option`);
    return false;
  }
  let flag = true;
  function checkRemote(opt) {
    const arr = Object.keys(opt);
    remoteConfig.forEach((e) => {
      if (arr.indexOf(e) == -1) {
        logger.error(`${JSON.stringify(opt)}中缺少参数${e}`);
        flag = false;
      }
    });
  }
  for (let item in cos) {
    // 获取key
    const arr = Object.keys(cos[item]);
    target.forEach((e) => {
      if (arr.indexOf(e) == -1) {
        logger.error(`${item}中缺少参数${e}`);
        flag = false;
      }
      if (e == "remote") {
        // remote 数组
        if (Array.isArray(cos[item][e])) {
          const optArr = cos[item][e];
          optArr.forEach(checkRemote);
        } else {
          // 对象
          checkRemote(cos[item][e]);
        }
      }
    });
  }

  return flag;
}

/**
 * 打标
 */
function logTag() {
  // 打标
  console.log(
    chalk.blueBright(
      figlet.textSync("creat by ymp", {
        font: "Standard",
        horizontalLayout: "full",
        verticalLayout: "default",
        width: 1000,
        whitespaceBreak: true,
      })
    )
  );
}

/**
 * 获取配置
 * @returns
 */
function getConfig() {
  const arr = glob.sync(`${process.cwd()}/publish.config.{js,cjs}`);
  let config = null;
  let types = [];
  let pointLog = false;
  let isZip = false;
  let isParallel = false;
  if (arr.length) {
    const filePath = arr[0];
    const { option, logs, zip, parallel } = require(filePath);
    if (option) config = option;
    if (logs) pointLog = logs;
    if (zip) isZip = zip;
    if (parallel) isParallel = parallel;
    if (!checkConf(config)) {
      return;
    } else {
      types = Object.keys(config);
    }
  } else {
    logger.error(`${process.cwd()}中没有publish.config.(c)js`);
    return;
  }
  return {
    types: types,
    isLog: pointLog,
    config: config,
    zip: isZip,
    parallel: isParallel,
  };
}
/**
 * 选择器
 * @param {*} types
 * @returns
 */
async function getSelect(types = []) {
  const { devType } = await inquirer.prompt([
    {
      name: "devType",
      type: "list",
      message: `请选择发布的测试版本.`,
      choices: types.filter((e) => {
        return {
          name: e,
          value: e,
        };
      }),
      default: types[0],
    },
  ]);
  return devType;
}
/**
 * 上传文件
 * @param {*} selectConfig
 */
async function updata(remote, localDir, other) {
  const ssh = new NodeSSH();
  other.upSpinner.start();
  logger.info(`连接服务器`);
  try {
    await ssh.connect({
      host: remote.host,
      username: remote.username,
      port: remote.port || 22,
      password: remote.passdword,
      tryKeyboard: true,
    });
    logger.info(`${remote.host}连接服务器成功`);
    logger.info(`开始上传文件`);
    const putStart = Date.now();
    if (other.zip) {
      const distDir = path.resolve(process.cwd(), zipName);
      const remoteZip = path.join(remote.remoteDir, zipName);
      logger.info(`${distDir}---->${remoteZip}`);

      // 上传前command
      if (remote.beforeUploadCommand) {
        await ssh
          .execCommand(remote.beforeUploadCommand, { cwd: remote.remoteDir })
          .then((result) => {
            if (result.stderr) {
              logger.error(result.stderr);
              other.upSpinner.fail();
              notify(result.stderr, "error");
            }
          });
      }

      // 上传zip
      await ssh.putFile(distDir, remoteZip, null, { concurrency: 1 });
      // 执行解压脚本
      await ssh
        .execCommand("tar -zxvf ./dist.tgz -C ./", { cwd: remote.remoteDir })
        .then((result) => {
          if (result.stderr) {
            logger.error(result.stderr);
            other.upSpinner.fail();
            notify(result.stderr, "error");
          }
        });
      // 删除zip
      await ssh
        .execCommand("rm -rf dist.tgz", { cwd: remote.remoteDir })
        .then((result) => {
          if (result.stderr) {
            logger.error(result.stderr);
            other.upSpinner.fail();
            notify(result.stderr, "error");
          }
        });
    } else {
      const distDir = path.resolve(process.cwd(), localDir);
      if (remote.beforeUploadCommand) {
        await ssh
          .execCommand(remote.beforeUploadCommand, { cwd: remote.remoteDir })
          .then((result) => {
            if (result.stderr) {
              logger.error(result.stderr);
              other.upSpinner.fail();
              notify(result.stderr, "error");
            }
          });
      }
      // 上传文件夹
      await ssh.putDirectory(distDir, remote.remoteDir, {
        transferOptions: { concurrency: 1 },
        tick: function (localPath, remotePath, error) {
          if (error) {
            logger.error(error);
          } else {
            logger.info(`传输：${localPath}  -> ${remotePath}`);
          }
        },
      });
    }
    if (remote.afterTaskCommand) {
      await ssh
        .execCommand(remote.afterTaskCommand, { cwd: remote.remoteDir })
        .then((result) => {
          if (result.stderr) {
            logger.error(result.stderr);
            other.upSpinner.fail();
            notify(result.stderr, "error");
          }
        });
    }
    ssh.dispose();
    const putEnd = Date.now();
    const time = `上传文件成功,耗时：${(putEnd - putStart) / 1000}s`;
    const adddress = `服务器：${remote.host},路径：${remote.remoteDir}${time}`;
    other.upSpinner.succeed();
    logger.info(adddress);
  } catch (err) {
    logger.error(err);
    other.upSpinner.fail();
    notify(err, "error");
  }
}

/**
 * 如果是数组，按顺序执行；如果是obj执行一次
 * @param {*} config
 */
async function loopUpdate(config, other = {}) {
  if (Array.isArray(config.remote)) {
    return new Promise((resolve) => {
      const loop = new EventLoop({ parallel: other.parallel });
      config.remote.forEach((e) => {
        loop.add(() => updata(e, config.localDir, other));
      });
      loop.start();
      loop.onEnd(resolve);
    });
  } else {
    await updata(config.remote, config.localDir, other);
  }
}

function notify(msg = "", type = "info") {
  if (typeof msg != "string") msg = JSON.stringify(msg);
  notifier.notify({
    title: `${pka.name}提示`,
    message: msg,
    sound: true,
    type: type, //type : info | warn | error
  });
}

module.exports = {
  loopUpdate,
  updata,
  getSelect,
  getConfig,
  logTag,
  checkConf,
  notify,
};
